package com.service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.elasticsearch.action.bulk.BulkItemResponse;
import org.elasticsearch.action.bulk.BulkRequestBuilder;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MatchPhraseQueryBuilder;
import org.elasticsearch.index.query.MultiMatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.sort.SortBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.pojo.ESBaseData;

/**
 * document相关增删改查api
 * 
 * @author shaoyuxia
 *
 * @param <T>
 */
public abstract class AbstractDataService<T extends ESBaseData> extends AbstractIndexService<T> {

	private static final Logger logger = LoggerFactory.getLogger(AbstractDataService.class);

	private Class<T> entityClass;

	/**
	 * 添加数据到数据所在索引，需要在数据内部指定index、type元数据,_id自动生成
	 * 
	 * @param data
	 * @return
	 */
	public AbstractDataService(Class<T> entityClass) {
		this.entityClass = entityClass;
	}

	public int postData(List<T> data) {
		return postData0(data, false);
	}

	private int postData0(List<T> data, boolean genDocid) {
		int fail = 0;
		if (data != null && !data.isEmpty()) {
			BulkRequestBuilder brb = client.prepareBulk();
			try {
				if (genDocid) {
					for (T d : data) {
						String json = mapper.writeValueAsString(d);
						brb.add(client.prepareIndex(d.get_indexName(), d.get_typeName(), d.get_id()).setSource(json,
								XContentType.JSON));
					}
				} else {
					for (T d : data) {
						String json = mapper.writeValueAsString(d);
						brb.add(client.prepareIndex(d.get_indexName(), d.get_typeName(), null).setSource(json,
								XContentType.JSON));
					}
				}
				BulkResponse resp = brb.execute().actionGet();
				if (resp.hasFailures()) {
					for (BulkItemResponse r : resp) {
						logger.error(r.getFailureMessage());
						fail++;
					}
				}
			} catch (JsonProcessingException e) {
				e.printStackTrace();
			}
		}
		return fail;
	}

	/**
	 * 添加数据到数据所在索引，需要在数据内部指定index、type、id元数据
	 * 
	 * @param data
	 * @return
	 */
	public int putData(List<T> data) {
		return postData0(data, true);
	}

	/**
	 * 添加数据到数据所在索引，需要在数据内部指定index、type元数据,_id自动生成
	 * 
	 * @param entity
	 * @return id
	 */
	public String post(T entity) {
		IndexResponse resp = client.prepareIndex(entity.get_indexName(), entity.get_typeName())
				.setSource(toJson(entity), XContentType.JSON).get();

		logger.debug("post status:" + resp.status() + "id:" + resp.getId());
		return resp.getId();
	}

	/**
	 * 添加数据到数据所在索引，需要在数据内部指定index、type、id元数据
	 * 
	 * @param entity
	 * @param docid
	 * @return
	 */
	public boolean put(T entity) {
		if (entity.get_id() == null) {
			return false;
		}
		IndexResponse resp = client.prepareIndex(entity.get_indexName(), entity.get_typeName(), entity.get_id())
				.setSource(toJson(entity), XContentType.JSON).get();
		logger.debug("put status :" + resp.status());
		return resp.status() == RestStatus.CREATED;
	}

	/**
	 * 获取doc
	 * 
	 * @param index
	 * @param type
	 * @param docid
	 * @return
	 */
	public T get(String index, String type, String docid) {
		GetResponse resp = client.prepareGet(index, type, docid).get();
		if (resp.isExists()) {
			String json = resp.getSourceAsString();
			logger.debug("response string:" + json);
			T entity = praseEntity(json);
			entity.set_id(resp.getId());
			return entity;
		}
		return null;
	}

	/**
	 * 删除doc
	 * 
	 * @param index
	 * @param type
	 * @param docid
	 * @return
	 */
	public boolean delete(String index, String type, String docid) {
		try {
			DeleteResponse resp = client.prepareDelete(index, type, docid).get();
			logger.debug("delete status " + resp.status());
			return resp.status() == RestStatus.OK;
		} catch (Exception e) {
			return false;
		}
	}

	public boolean delete(T t) {
		try {
			DeleteResponse resp = client.prepareDelete(t.get_indexName(), t.get_typeName(), t.get_id()).get();
			logger.debug("delete status " + resp.status());
			return resp.status() == RestStatus.OK;
		} catch (Exception e) {
			return false;
		}
	}

	/**
	 * 更新doc
	 * 
	 * @param index
	 * @param type
	 * @param docid
	 * @param json
	 * @return
	 */
	public boolean update(T entity) {
		UpdateResponse resp = client.prepareUpdate(entity.get_indexName(), entity.get_typeName(), entity.get_id())
				.setDoc(toJson(entity), XContentType.JSON).get();
		logger.debug("update status " + resp.status());
		return resp.status() == RestStatus.OK;
	}

	public String toJson(T entity) {
		try {
			return mapper.writeValueAsString(entity);
		} catch (JsonProcessingException e) {
			logger.error(e.getMessage());
			return "";
		}
	}

	private T praseEntity(String json) {
		try {
			return mapper.readValue(json, entityClass);
		} catch (Exception e) {
			logger.error(e.getMessage());
			return null;
		}
	}

	/**
	 * term 查询
	 * 
	 * @param index
	 * @param field
	 * @param keyword
	 * @param sorter
	 * @return
	 */
	public List<T> termQuery(String index, String field, String keyword, SortBuilder<?> sorter) {
		List<T> result = new ArrayList<>();
		TermQueryBuilder qb = new TermQueryBuilder(field, keyword);
		System.out.println(qb.toString());
		SearchRequestBuilder req = client.prepareSearch(index).setQuery(qb);
		if (sorter != null) {
			req.addSort(sorter);
		}
		logger.info(req.toString());
		SearchResponse resp = req.execute().actionGet();
		resp.getHits().forEach(hit -> {
			logger.info(hit.getSourceAsString());
			T entity = praseEntity(hit.getSourceAsString());
			entity.set_id(hit.getId());
			result.add(entity);
		});
		return result;
	}

	/**
	 * 短语查询
	 * 
	 * @param index
	 * @param field
	 * @param phase
	 * @param sorter
	 * @return
	 */
	public List<T> phaseQuery(String index, String field, String phase, SortBuilder<?> sorter) {
		Map<String, String> phases = new HashMap<String, String>();
		phases.put(field, phase);
		return multiPhaseQuery(index, phases, sorter);
	}

	/**
	 * 多个短语查询
	 * 
	 * @param index
	 * @param phases
	 * @param sorter
	 * @return
	 */
	public List<T> multiPhaseQuery(String index, Map<String, String> phases, SortBuilder<?> sorter) {
		List<T> result = new ArrayList<>();
		BoolQueryBuilder boolqb = QueryBuilders.boolQuery();
		for (Entry<String, String> entry : phases.entrySet()) {
			MatchPhraseQueryBuilder qb = QueryBuilders.matchPhraseQuery(entry.getKey(), entry.getValue());
			boolqb.should(qb);
		}
		SearchRequestBuilder req = client.prepareSearch(index).setQuery(boolqb);
		logger.info(req.toString());
		if (sorter != null) {
			req.addSort(sorter);
		}
		SearchResponse resp = req.get();
		SearchHits hits = resp.getHits();
		hits.forEach(hit -> {
			logger.info(hit.getSourceAsString());
			T entity = praseEntity(hit.getSourceAsString());
			entity.set_id(hit.getId());
			result.add(entity);
		});
		return result;
	}
	/**
	 * 多个field的match查询
	 * @param index
	 * @param text
	 * @param sorter
	 * @param fields
	 * @return
	 */
	public List<T> multiFieldQuery(String index, String text, SortBuilder<?> sorter, String... fields) {
		List<T> result = new ArrayList<>();
		MultiMatchQueryBuilder qb = QueryBuilders.multiMatchQuery(text, fields);
		System.out.println(qb.toString());
		SearchRequestBuilder req = client.prepareSearch(index).setQuery(qb);
		if (sorter != null) {
			req.addSort(sorter);
		}
		logger.info(req.toString());
		req.get().getHits().forEach(hit -> {
			logger.info(hit.getSourceAsString());
			T entity = praseEntity(hit.getSourceAsString());
			entity.set_id(hit.getId());
			result.add(entity);
		});
		return result;
	}

}
